﻿// TradeSpi.h: interface for the CTradeSpi class.
//
//////////////////////////////////////////////////////////////////////

#ifndef TRADESPI_H
#define TRADESPI_H



#include "string.h"
#include "ThostFtdcTraderApi.h"
#include "ThostFtdcUserApiStruct.h"
#include "Config.h"
#include "Lock.h"
#include "mystruct.h"
#include <map>
#include <set>
#include <vector>
#include <string>
#include "qtapi.h"
#include "Event.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "mylogger.h"

using namespace std;

class CTradeSpi;
class CFClient;

#define MAX_ORDERS 100
#define TRADES_COUNT 100
#define MSG_FINSTRUMENT_SATATUS2 2

class IInsPosition{
public:
	bool	m_bIsSHFE;
	bool	m_bIsCZCE;
	char	m_cIns[32];
	char	m_cTradingDay[16];

	int		LongVolume;
	int		LongTodayVolume;
	int		LongTotalClosable;
	int		LongTodayClosable;
	double	LongOpenAmmount;
	double	LongPositionAmmount;
	double	LongCloseProfit;
	double	LongPositionProfit;
	int		ShortVolume;
	int		ShortTodayVolume;
	int		ShortTotalClosable;
	int		ShortTodayClosable;
	double	ShortOpenAmmount;
	double	ShortPositionAmmount;
	double	ShortCloseProfit;
	double	ShortPositionProfit;

	int		m_nMultiply;

public:
	IInsPosition(){
		LongVolume = 0;
		LongTodayVolume = 0;
		LongTotalClosable = 0;
		LongTodayClosable = 0;
		LongOpenAmmount = 0;
		LongPositionAmmount = 0;
		LongCloseProfit = 0;

		ShortVolume = 0;
		ShortTodayVolume = 0;
		ShortTotalClosable = 0;
		ShortTodayClosable = 0;
		ShortOpenAmmount = 0;
		ShortPositionAmmount = 0;
		ShortCloseProfit = 0;
		m_nMultiply = 1;

		LongCloseProfit = 0;
		LongPositionProfit = 0;

		ShortCloseProfit = 0;
		ShortPositionProfit = 0;
	}

	virtual ~IInsPosition(){

	}

	virtual void Lock()=0;

	virtual void Unlock()=0;
};

struct TimerField{
	CtpOrderField*	pOrder;
	int				Action;
	int				SecondsAfter;
	IPolicy* 		pListener;
};

class CInsPosition:public IInsPosition{
public:
	double OpenCommissionRateByVolume;
	double OpenCommissionRateByMoney;
	double CloseCommissionRateByMoney;
	double CloseCommissionRateByVolume;
	double CloseTodayCommissionRateByMoney;
	double CloseTodayCommissionRateByVolume;

	double Commission;
	double ClosedAmmount;
	double ClosedTodayAmmount;
	double OpenedAmmount;

	int    OpenedVolume;
	int    ClosedVolume;
	int    ClosedTodayVolume;

	double LongMarginRateByMoney;
	double ShortMarginRateByMoney;

	double LongMarginByVolume;
	double ShortMarginByVolume;

private:	
	CLock							m_Lock;
	list<PositionDetailField*>		m_lTdLong;
	list<PositionDetailField*>		m_lYdLong;

	list<PositionDetailField*>		m_lTdShort;
	list<PositionDetailField*>		m_lYdShort;

private:

	void AddPositionDetail(list<PositionDetailField*>* pList,int nVolume,double fOpenPrice,double fPositionPrice,const char *pszOpenDate,const char *pszTradeID,bool bNewOpen=false);

	void CloseLongPositionDetail(list<PositionDetailField*>* pList,CThostFtdcTradeField* pField);

	void CloseShortPositionDetail(list<PositionDetailField*>* pList,CThostFtdcTradeField* pField);

public:
	CInsPosition(const char *pszIns,bool bIsSHFE,const char *pszTradingDay,int nMultiply);

	virtual ~CInsPosition();

	inline void UpdatePosition(CThostFtdcTradeField* pField);

	inline void AddPositionDetail(CThostFtdcInvestorPositionField* pPosDetail);

	inline void AddPositionDetail(CThostFtdcTradeField* pField,bool bIsLong);

	inline void ClosePositionDetail(CThostFtdcTradeField* pField);

	inline void GetLongPos(int& nTotal,int& nClosable,int& nTodayClosable){
		nTotal = LongVolume;
		nClosable = LongTotalClosable;
		nTodayClosable = LongTodayClosable;
	}

	inline void GetLongPos(int& nTotal,int& nClosable,int& nTodayClosable,double& fAvgOpenPrice){
		nTotal = LongVolume;
		nClosable = LongTotalClosable;
		nTodayClosable = LongTodayClosable;
		if(LongVolume>0){
			fAvgOpenPrice = LongOpenAmmount/LongVolume;
		}else{
			fAvgOpenPrice = 0;
		}
	}

	inline void GetShortPos(int& nTotal,int& nClosable,int& nTodayClosable){
		nTotal = ShortVolume;
		nClosable = ShortTotalClosable;
		nTodayClosable = ShortTodayClosable;
	}

	inline void GetShortPos(int& nTotal,int& nClosable,int& nTodayClosable,double& fAvgOpenPrice){
		nTotal = ShortVolume;
		nClosable = ShortTotalClosable;
		nTodayClosable = ShortTodayClosable;
		if(ShortVolume>0){
			fAvgOpenPrice = ShortOpenAmmount/ShortVolume;
		}else{
			fAvgOpenPrice = 0;
		}

	}

	inline void GetPos(int& nLong,int& nLongClosable,int nLongTodayClosable,int& nShort,int& nShortClosable,int nShortTodayClosable){
		nLong = LongVolume;
		nLongClosable = LongTotalClosable;
		nLongTodayClosable = LongTodayClosable;

		nShort = ShortVolume;
		nShortClosable = ShortTotalClosable;
		nShortTodayClosable = ShortTodayClosable;
	}

	//todo 
	void CalProfit(){
		

	}

	//todo
	void CalCommission(){
		
	}
	
	void Lock(){
		m_Lock.Lock();
	}

	void Unlock(){
		m_Lock.Unlock();
	}
};


class CTradeSpi : public CThostFtdcTraderSpi
{
public:
	//CThostFtdcBrokerTradingParamsField	m_Params;
	map<string,InstrumentField*>		m_Instruments;
	map<string,CInsPosition*>			m_Positions;
	CThostFtdcTradingAccountField	m_Account;
	CLock m_AcctLock;

	void* m_pFutureAccount;

	time_t  m_tLastTradeTime;
	int 	m_bOnline;
	CMyConfig2				m_Config;

//attributes;
private:
	bool					m_bInBg;
	int						m_nLastError;
	char					m_cLastError[256];
	char					m_cInitFile[256];
	int 					m_nSeconds;
	char 					m_cCurTime[12];


	time_t					m_tLastOrderTime;
	time_t					m_tLastQueryAcctTime;


	CThostFtdcTraderApi*		m_pTradeApi;

	CSpinLock				m_PosLock;
	CSpinLock				m_OrderLock;

	int						m_nOrdersCount;
	int						m_nOrdersCreated;
	CtpOrderField*			m_pOrders[MAX_ORDERS+1];
	map<string,CtpOrderField*>	m_OrdersByLocalID;
	map<string,CtpOrderField*>	m_OrdersBySysID;

	int						m_nNextTrade;
	TradeField				m_TradeFields[TRADES_COUNT];

	map<string,string>		m_LatestTradeID;
	set<string>				m_TradeIDs;	

	map<string,list<CThostFtdcTradeField*>*>		m_CachedTrades;

	CLock							m_OALock;
	map<int,int>					m_FailedOA;

	//market data cache;
	CLock			m_MdLock;
	map<string,MarketDataField*> m_LastMarketData;

	//log buffers;
	//LogMsg	m_LogMsg[256];
	int		m_nNextMsg;
	CLock	m_lockMsg;

	char m_cInvestorID[32];
	int		m_nFrontID;
	int		m_nSessionID;

	CEvent m_hQueryFinished;
	list<CFClient*>		m_Clients;
	map<string, IPolicy*> policies;   // <OrderRef, Policy>
	map<string, CtpOrderField*> ctpOrderFields; // <OrderRef, CtpOrderField>
	CLock m_ClientsLock;

	CSpinLock   m_coclock;
	map<string,int>  m_CancelOrdersCount;

	FILE * m_pfLog;
	int    m_nSeed;

	// my variable
	bool   m_connected;
	bool b_authenticated;
	bool b_position;
    bool b_instruments;
	//新加3个参数
//std::string pszAppID;
//std::string pszAuthCode;
//std::string pszProductInfo;
//新增持仓
    std::unordered_map<std::string, CThostFtdcInvestorPositionField> long_pos_map_;
    std::unordered_map<std::string, CThostFtdcInvestorPositionField> short_pos_map_;
//品种信息
    std::unordered_map<std::string, CThostFtdcInstrumentField> future_instruments_;


	vector<IPolicy*> v_policy;

private:

	void CacheTrade(CThostFtdcTradeField* pField);

	void FlushCachedTrade(CtpOrderField* pOrder);

	CtpOrderField* GetOrderByBrokerOID(const char* pszBrokerOrderID);

	double GetLastPrice(const char *psz){
		double fPrice=0;
		m_MdLock.Lock();
		map<string,MarketDataField*>::iterator it = m_LastMarketData.find(psz);
		if(it != m_LastMarketData.end()){
			MarketDataField* pData = it->second;
			fPrice = pData->LastPrice;
		}
		m_MdLock.Unlock();
		return fPrice;
	}

	double GetAveragePrice(const char *psz){
		double fPrice=0;
		m_MdLock.Lock();
		map<string,MarketDataField*>::iterator it = m_LastMarketData.find(psz);
		if(it != m_LastMarketData.end()){
			MarketDataField* pData = it->second;
			fPrice = pData->AveragePrice;
		}
		m_MdLock.Unlock();
		return fPrice;
	}

	double GetPreSettlementPrice(const char *psz){
		double fPrice=0;
		m_MdLock.Lock();
		map<string,MarketDataField*>::iterator it = m_LastMarketData.find(psz);
		if(it != m_LastMarketData.end()){
			MarketDataField* pData = it->second;
			fPrice = pData->PreSettlementPrice;
		}
		m_MdLock.Unlock();
		return fPrice;
	}

	//query instruments;
	int QueryAccount();
	
	//query instruments;
	//int QueryInstruments();

	//query detail positions;
	//int QueryDetailPositions();

	int ReqClientLogin(const char *pszAuthCode);

	int ReqUserLogin();

	//穿透式监管
	int ReqAuthenticate();
	//void OnRspDSUserCertification(CUstpFtdcDSUserCertRspDataField *pDSUserCertRspData, CUstpFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);
	void OnRspAuthenticate(CThostFtdcRspAuthenticateField *pRspAuthenticateField, CThostFtdcRspInfoField *pRspInfo,
                              int nRequestID, bool bIsLast);
							  
//    void OnRspQryInstrument(CUstpFtdcRspInstrumentField *pRspInstrument, CUstpFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);
void OnRspQryInstrument(CThostFtdcInstrumentField *pInstrument, CThostFtdcRspInfoField *pRspInfo, int nRequestID,
                               bool bIsLast);
							   
//    void OnRspQryInvestorPosition(CUstpFtdcRspInvestorPositionField *pRspInvestorPosition, CUstpFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);
void OnRspQryInvestorPosition(CThostFtdcInvestorPositionField *pInvestorPosition,
                                                   CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);
												   
void OnRspSettlementInfoConfirm(CThostFtdcSettlementInfoConfirmField *pSettlementInfoConfirm,
                                                     CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);
    bool req_position();
	bool req_settlement_confirm();
    bool req_qry_instrument();


	void ReqUserInvestor();

    int ReqUserInvestor_();

	//confirm settlement;
	//int Confirm();

	//update local position cache;
	inline void UpdatePosition(CThostFtdcTradeField *pField);

	//check whether an instrument is of SHFE exchange;
	inline bool IsSHFE(const char *pszIns);

	//frozen position when a close order is sent;
	void FrozenPosition(CtpOrderField* pOrder,const char *pszCode,char cBuySell,char cClose,int nVolume);

	//unfrozend position when a close order is canceled;
	void UnfrozenPosition(CtpOrderField* pOrder);

	//get next trade field;
	TradeField* GetNextTradeField();

public:
	CTradeSpi(const char *pszInitFile=NULL);
	virtual ~CTradeSpi();

	void AddPolicy(IPolicy* newPolicy)
	{
		v_policy.push_back(newPolicy);
	}

	void DeletePolicy(IPolicy* oldPolicy)
	{
		vector<IPolicy*>::iterator iter = v_policy.begin();
		for (; iter != v_policy.end(); iter++)
		{
			if (*iter == oldPolicy)
				break;
		}

		v_policy.erase(iter);
	}

	int GetCancelOrdersCount(const char* pszIns){
		int nCount=0;
		m_coclock.Lock();
		map<string,int>::iterator it = m_CancelOrdersCount.find(pszIns);
		if(it != m_CancelOrdersCount.end()){
			nCount = it->second;
		}
		m_coclock.Unlock();

		return nCount;
	}

	void IncCancelOrdersCount(const char* pszIns){
		m_coclock.Lock();
		map<string,int>::iterator it = m_CancelOrdersCount.find(pszIns);
		if(it != m_CancelOrdersCount.end()){
			it->second++;
		}else{
			m_CancelOrdersCount.insert(pair<string,int>(pszIns,1));
		}
		m_coclock.Unlock();
	}
	void PostLogMessage(const char *pszLog,int nLevel);
	void OpenLog(){
		char cFileName[256];
		char cDate[16];

		time_t t;
		time(&t);

		struct tm* pTm = localtime(&t);
		strftime(cDate,15,"%Y%m%d",pTm);

		strcpy(cFileName,"./logs/");
		strcat(cFileName,m_Config.GetUser());
		strcat(cFileName,"_");
		strcat(cFileName,cDate);
		strcat(cFileName,".log");

		m_pfLog = fopen(cFileName,"at+");
	}


	/**
	 *login to trade;
	 */
	virtual int Login(const char* UserID,const char* Password,char* ErrorMsg,int* ErrorID);

	virtual int Logout(){
		if(m_pTradeApi){
			m_pTradeApi->RegisterSpi(NULL);

			CThostFtdcUserLogoutField reqUserLogout;
			memset(&reqUserLogout,0,sizeof(CThostFtdcUserLogoutField));

			strcpy(reqUserLogout.BrokerID, m_Config.GetBroker());
			strcpy(reqUserLogout.UserID, m_Config.GetUser());

			int nTried = 0;
			int retCode = m_pTradeApi->ReqUserLogout(&reqUserLogout, 2);

			while(retCode<0 && nTried++ <4){
				sleep(1);
				retCode = m_pTradeApi->ReqUserLogout(&reqUserLogout, 2);
			}
		}
		return 1;
	}

	virtual IFutureTrader* CreateTrader(IPolicy* pPolicy,char cHedgeFlag='1',bool IsSHFE=false);

	void Stop(){
		if(m_pTradeApi != NULL){
			m_pTradeApi->RegisterSpi(NULL);
			m_pTradeApi->Release();
			m_pTradeApi = NULL;
		}
	}

	virtual void Release(){
		delete this;
	}

	int GetLastErrorID();

	char* GetLastErrorMsg();

	void Log(const char *pszLog,int nLevel,long ThreadID);

	void CloseLog();

	virtual void Debug(const char *pszLog);

	virtual void Info(const char *pszLog);

	virtual void Error(const char *pszLog);

	//LogMsg* GetLogMsg();

	virtual void OnFutureMarketData(MarketDataField* pData);

	void OnFrontConnected();

	void OnRspError(CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);
	//{
	//	if(pRspInfo){
	//		printf("onrsp error: %d %s\n",pRspInfo->ErrorID,pRspInfo->ErrorMsg);
	//	}
	//}

	void OnRspUserLogin(CThostFtdcRspUserLoginField *pRspUserLogin, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);

	void OnFrontDisconnected(int nReason);

	void OnRspQryInvestor(CThostFtdcInvestorField *pRspUserInvestor, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);

	void OnRspQryTradingAccount(CThostFtdcTradingAccountField *pTradingAccount, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);

	//void OnRspQryBrokerTradingParams(CThostFtdcBrokerTradingParamsField *pBrokerTradingParams, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);

	virtual void OnRtnOrder(CThostFtdcOrderField *pOrder);

	virtual void OnRtnTrade(CThostFtdcTradeField *pOrder);

	///合约交易状态通知
	virtual void OnRtnInstrumentStatus(CThostFtdcInstrumentStatusField *pInstrumentStatus);

	virtual void OnRspOrderInsert(CThostFtdcInputOrderField *pInputOrder, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);

	virtual void OnRspOrderAction(CThostFtdcInputOrderActionField *pInputOrderAction, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);

	virtual void OnErrRtnOrderInsert(CThostFtdcInputOrderField *pInputOrder, CThostFtdcRspInfoField *pRspInfo);

	virtual void OnErrRtnOrderAction(CThostFtdcInputOrderActionField *pOrderAction, CThostFtdcRspInfoField *pRspInfo);

	const char * GetInitFile();


	const char * GetTradingDay(){
		return this->m_Config.GetTradingDay();
	}
	//create emtpy new orders for late use;
	void CreateEmptyOrders();

	bool IsFinished(CtpOrderField* pOrder);

	CtpOrderField* GetOrderByID(int nOrderID);

	CtpOrderField* GetNewOrder();

	CtpOrderField* GetNewOrderNoLock();

	CtpOrderField* GetNewOrderByID(int nOrderID);

	int CheckMyOrderID(const char * psz);

	//login to the md server;
	int Login(const char *pszUserID,const char *pszPassword);

	// set TimeCondition = '1' only for FAK
	// set TimeCondition = '1' & VolCondition = '3' for FOK
	inline CtpOrderField* Buy(const char *pszCode,int nVolume,double fPrice,IPolicy *pOrderListener=NULL,char HedgeFlag=THOST_FTDC_HFEN_Speculation,char cPriceType='2',char cTimeCondition='3',char cVolCondition='1',int nMinVolume=1){
		return PlaceOrder(pszCode,THOST_FTDC_D_Buy,THOST_FTDC_OF_Open,nVolume,fPrice,pOrderListener,HedgeFlag,cPriceType,cTimeCondition,cVolCondition,nMinVolume);
	}

	inline CtpOrderField* Short(const char *pszCode,int nVolume,double fPrice,IPolicy *pOrderListener=NULL,char HedgeFlag=THOST_FTDC_HFEN_Speculation,char cPriceType='2',char cTimeCondition='3',char cVolCondition='1',int nMinVolume=1){
		return PlaceOrder(pszCode,THOST_FTDC_D_Sell,THOST_FTDC_OF_Open,nVolume,fPrice,pOrderListener,HedgeFlag,cPriceType,cTimeCondition,cVolCondition,nMinVolume);
	}

	inline CtpOrderField* Sell(const char *pszCode,int nVolume,double fPrice,bool bIsSHFE=false,IPolicy *pOrderListener=NULL,char HedgeFlag=THOST_FTDC_HFEN_Speculation,char cPriceType='2',char cTimeCondition='3',char cVolCondition='1',int nMinVolume=1){
		return PlaceOrder(pszCode,THOST_FTDC_D_Sell,THOST_FTDC_OF_Close,nVolume,fPrice,pOrderListener,HedgeFlag,cPriceType,cTimeCondition,cVolCondition,nMinVolume);
	}

	inline CtpOrderField* Cover(const char *pszCode,int nVolume,double fPrice,bool bIsSHFE=false,IPolicy *pOrderListener=NULL,char HedgeFlag=THOST_FTDC_HFEN_Speculation,char cPriceType='2',char cTimeCondition='3',char cVolCondition='1',int nMinVolume=1){
		return PlaceOrder(pszCode,THOST_FTDC_D_Buy,THOST_FTDC_OF_Close,nVolume,fPrice,pOrderListener,HedgeFlag,cPriceType,cTimeCondition,cVolCondition,nMinVolume);
	}

	inline CtpOrderField* PlaceOrder(const char *pszCode,char cBuySell,char cOpenClose,int nVolume,double fPrice,IPolicy *pPolicy=NULL,char HedgeFlag=THOST_FTDC_HFEN_Speculation,char cPriceType='2',char cTimeCondition='3',char cVolCondition='1',int nMinVolume=1){
		int nRetCode;

		CThostFtdcInputOrderField* pInputOrderField =new CThostFtdcInputOrderField();
		//todo set fields;
		strcpy(pInputOrderField->BrokerID, m_Config.GetBroker());
		strcpy(pInputOrderField->UserID, m_Config.GetUser());
		//strcpy(pInputOrderField->ExchangeID, "CFFEX");
		strcpy(pInputOrderField->InvestorID, m_Config.GetInvestorID());
		strcpy(pInputOrderField->InstrumentID, pszCode);

        m_OrderLock.Lock();

		if(b_instruments) {
            auto iter = future_instruments_.find(std::string(pszCode));
            if (iter != future_instruments_.end())
            {
                strcpy(pInputOrderField->ExchangeID, iter->second.ExchangeID);
            }else{
                //do nothing, waiting for  OnRspOrderInsert
            }
		}else{
            //do nothing, waiting for  OnRspOrderInsert
		}
/*
		if (pszCode[0] >= 'A' && pszCode[0] <= 'Z')
		{
			strcpy(pInputOrderField->ExchangeID, "CFFEX");
		}
		else
		{
			strcpy(pInputOrderField->ExchangeID, "SHFE");
		}
*/

		//strcpy(pInputOrderField->OrderRef, "TODO");
		pInputOrderField->OrderPriceType = cPriceType;
		pInputOrderField->Direction = cBuySell;
		pInputOrderField->CombOffsetFlag[0] = cOpenClose;
		pInputOrderField->CombHedgeFlag[0] = HedgeFlag;
		pInputOrderField->LimitPrice = fPrice;
		pInputOrderField->VolumeTotalOriginal = nVolume;
		pInputOrderField->TimeCondition = cTimeCondition;
		pInputOrderField->VolumeCondition = cVolCondition;
		pInputOrderField->MinVolume = nMinVolume;
		pInputOrderField->ForceCloseReason = THOST_FTDC_FCC_NotForceClose;
		//ctp add more
        pInputOrderField->StopPrice = 0;
        pInputOrderField->IsAutoSuspend = true;
        pInputOrderField->ContingentCondition = THOST_FTDC_CC_Immediately;

		int nReqID = m_Config.GetNextRequestID();
		long orderRef = m_Config.GetAndIncMaxOrderRef();

		sprintf(pInputOrderField->OrderRef, "%d", orderRef);

		CtpOrderField* pOrder = new CtpOrderField();
		memset(pOrder, 0, sizeof(CtpOrderField));
		strcpy(pOrder->BrokerID, pInputOrderField->BrokerID);
		pOrder->Direction = pInputOrderField->Direction;
		strcpy(pOrder->ExchangeID, pInputOrderField->ExchangeID);
		pOrder->pInputOrderField = pInputOrderField;
		strcpy(pOrder->UserID, m_Config.GetUser());
		strcpy(pOrder->InvestorID, m_Config.GetInvestorID());
		strcpy(pOrder->InstrumentID, pszCode);
		pOrder->Volume = nVolume;
		strcpy(pOrder->UserOrderLocalID, pInputOrderField->OrderRef);

		policies.insert(make_pair(pInputOrderField->OrderRef, pPolicy));
		ctpOrderFields.insert(make_pair(pInputOrderField->OrderRef, pOrder));

		if (policies.find(pInputOrderField->OrderRef) != policies.end()
			&& ctpOrderFields.find(pInputOrderField->OrderRef) != ctpOrderFields.end())
		{
			SPDLOG_INFO("start to insert order. OrderID = {}" , pInputOrderField->OrderRef);
		}

		nRetCode = m_pTradeApi->ReqOrderInsert(pInputOrderField, nReqID);
		SPDLOG_INFO("PlaceOrder nRetCode: {} ", (nRetCode == 0 ? "OK" : "Fail"));
        SPDLOG_INFO("BrokerID = {}", pInputOrderField->BrokerID);
        SPDLOG_INFO("UserID = {}", pInputOrderField->UserID);
        SPDLOG_INFO("InvestorID = {}", pInputOrderField->InvestorID);
        SPDLOG_INFO("InstrumentID = {}", pInputOrderField->InstrumentID);
        SPDLOG_INFO("ExchangeID = {}", pInputOrderField->ExchangeID);
        SPDLOG_INFO("OrderPriceType = {}", pInputOrderField->OrderPriceType);
        SPDLOG_INFO("Direction = {}", pInputOrderField->Direction);
        SPDLOG_INFO("CombOffsetFlag[0] = {}", pInputOrderField->CombOffsetFlag[0]);
        SPDLOG_INFO("CombHedgeFlag[0] = {}", pInputOrderField->CombHedgeFlag[0]);
        SPDLOG_INFO("LimitPrice = {}", pInputOrderField->LimitPrice);
        SPDLOG_INFO("VolumeTotalOriginal = {}", pInputOrderField->VolumeTotalOriginal);
        SPDLOG_INFO("TimeCondition = {}", pInputOrderField->TimeCondition);
        SPDLOG_INFO("VolumeCondition = {}", pInputOrderField->VolumeCondition);
        SPDLOG_INFO("MinVolume = {}", pInputOrderField->MinVolume);

        SPDLOG_INFO("OrderRef = {}", pInputOrderField->OrderRef);
        SPDLOG_INFO("User = {}", pInputOrderField->UserID);
        SPDLOG_INFO("User = {}", pInputOrderField->UserID);

		m_OrderLock.Unlock();

		return pOrder;
	}

	inline bool CancelOrder(CtpOrderField* pCtpOrder,bool withChild=true){
		if(pCtpOrder->OrderStatus == THOST_FTDC_OST_Canceled
			|| pCtpOrder->OrderStatus == THOST_FTDC_OST_AllTraded
			|| pCtpOrder->OrderStatus == THOST_FTDC_OST_PartTradedNotQueueing
			|| pCtpOrder->OrderStatus == THOST_FTDC_OST_NoTradeNotQueueing){
			
			return false;
		}


		m_OrderLock.Lock();
		CThostFtdcInputOrderActionField field;
		memset(&field,0,sizeof(CThostFtdcInputOrderActionField));
		strcpy(field.BrokerID,pCtpOrder->BrokerID);
		//printf("del order :%s\n",pCtpOrder->BrokerID);
		strcpy(field.UserID,pCtpOrder->UserID);
		strcpy(field.InvestorID,pCtpOrder->InvestorID);
		strcpy(field.OrderRef, pCtpOrder->UserOrderLocalID);

		field.ActionFlag = THOST_FTDC_AF_Delete;

		//long orderRef = m_Config.GetAndIncMaxOrderRef();
		//sprintf(field.OrderLocalID, "%d", orderRef);

		if(pCtpOrder->ExchangeID[0]!='\0'){
			strcpy(field.ExchangeID,pCtpOrder->ExchangeID);
		}
		if(pCtpOrder->OrderSysID[0]!='\0'){
			strcpy(field.OrderSysID,pCtpOrder->OrderSysID);
		}

        SPDLOG_INFO("CancelOrder ExchangeID = {} ", field.ExchangeID);
        SPDLOG_INFO("CancelOrder BrokerID = {} ", field.BrokerID);
        SPDLOG_INFO("CancelOrder UserID = {} ", field.UserID);
        SPDLOG_INFO("CancelOrder InvestorID = {} ", field.InvestorID);
        SPDLOG_INFO("CancelOrder OrderRef = {} ", field.OrderRef);
        SPDLOG_INFO("CancelOrder OrderSysID = {} ", field.OrderSysID);


		int nReqID = m_Config.GetNextRequestID();
		int nRetCode = m_pTradeApi->ReqOrderAction(&field, nReqID);
		SPDLOG_INFO("action nReqID = {} , retCode = {}", nReqID, nRetCode);
		m_OrderLock.Unlock();

		pCtpOrder->Canceling = 1;

		//m_tLastOrderTime = time(NULL);

		return true;
	}

	void GetLongPos(const char *pszIns,int& nTotal,int& nClosable,int& nTodayClosable);

	void GetShortPos(const char *pszIns,int& nTotal,int& nClosable,int& nTodayClosable);

	void GetLongPos(const char *pszIns,int& nTotal,int& nClosable,int& nTodayClosable,double& fAvgOpenPrice);

	void GetShortPos(const char *pszIns,int& nTotal,int& nClosable,int& nTodayClosable,double& fAvgOpenPrice);


	struct InstrumentField* GetInstrument(const char *pszInstrument);

	bool GetInstrument(FutureInstrumentField* pIns);

	void GetInstrument(const char* pszProductID,FutureInstrumentField** pIns,int* pCount);

	int GetMultiply(const char *pszInstrument);

	struct CThostFtdcRspInstrumentField* GetCtpInstrument(const char *pszInstrument);

	void SetBgTaskState(bool bInBg);

	void OnOneSecond();

	void RegisterTimer(CtpOrderField* pField,int nAction,int nSecondsAfter,IPolicy* pListener);

	void GetLongAvgPrice(const char *pszIns,double& fAvgOpenPrice,double& fAvgPositionPrice);

	void GetShortAvgPrice(const char *pszIns,double& fAvgOpenPrice,double& fAvgPositionPrice);
};


class CFClient: public IFutureTrader{
private:
	bool			m_bIsSHFE;
	char			m_cHedgeType;
	CTradeSpi*		m_pTradeSpi;

public:
	void* m_pFutureAcct;
	IPolicy*		m_pPolicy;

public:
	CFClient(){};

	CFClient(IPolicy* pPolicy,CTradeSpi* pTradeSpi,bool bIsSHFE,char cHedgeType);

	virtual ~CFClient(){ if (m_pTradeSpi != NULL) m_pTradeSpi->DeletePolicy(m_pPolicy); };

	IFutureAccount* GetFutureAccount(){
		return (IFutureAccount*)m_pFutureAcct;
	}
	CtpOrderField*	Buy(const char *pszCode,int nVolume,double fPrice,char cPriceType='2',char cTimeCondition='3',char cVolCondition='1',int nMinVolume=0){
		return m_pTradeSpi->Buy(pszCode,nVolume,fPrice,m_pPolicy,m_cHedgeType,cPriceType,cTimeCondition,cVolCondition,nMinVolume);
	}

	virtual OrderField* Buy( const char* InstrumentID,int nVolume,double fPrice );

	CtpOrderField*	Sell(const char *pszCode,int nVolume,double fPrice,char cPriceType='2',char cTimeCondition='3',char cVolCondition='1',int nMinVolume=0){
		return m_pTradeSpi->Sell(pszCode,nVolume,fPrice,m_bIsSHFE,m_pPolicy,m_cHedgeType,cPriceType,cTimeCondition,cVolCondition,nMinVolume);
	}

	virtual OrderField* Sell( const char* InstrumentID,int nVolume,double fPrice,int closeTd );

	CtpOrderField*	Short(const char *pszCode,int nVolume,double fPrice,char cPriceType='2',char cTimeCondition='3',char cVolCondition='1',int nMinVolume=0){
		return m_pTradeSpi->Short(pszCode,nVolume,fPrice,m_pPolicy,m_cHedgeType,cPriceType,cTimeCondition,cVolCondition,nMinVolume);
	}

	virtual OrderField* Short( const char* InstrumentID,int nVolume,double fPrice );

	CtpOrderField*	Cover(const char *pszCode,int nVolume,double fPrice,char cPriceType='2',char cTimeCondition='3',char cVolCondition='1',int nMinVolume=0){
		return m_pTradeSpi->Cover(pszCode,nVolume,fPrice,m_bIsSHFE,m_pPolicy,m_cHedgeType,cPriceType,cTimeCondition,cVolCondition,nMinVolume);
	}

	virtual OrderField* Cover( const char* InstrumentID,int nVolume,double fPrice,int closeTd );

	bool CancelOrder(OrderField* pOrder){
		return m_pTradeSpi->CancelOrder((CtpOrderField*)pOrder);
	}

	virtual void GetTradingAccountInfo(double* PreBalance,double* CloseProfit,double* PositionProfit,double* Commission,double* Balance,double* Available){
		m_pTradeSpi->m_AcctLock.Lock();
		*PreBalance =  m_pTradeSpi->m_Account.PreBalance;
		*CloseProfit =  m_pTradeSpi->m_Account.CloseProfit;
		*PositionProfit =  m_pTradeSpi->m_Account.PositionProfit;
		*Commission =  m_pTradeSpi->m_Account.Commission;
		*Balance =  m_pTradeSpi->m_Account.Balance;
		*Available =  m_pTradeSpi->m_Account.Available;
		m_pTradeSpi->m_AcctLock.Unlock();
	}

	virtual void GetTradingAccountInfo2(double* PreBalance,double* CloseProfit,double* PositionProfit,double* Margin,double* Commission,double* Balance,double* Available){
		m_pTradeSpi->m_AcctLock.Lock();
		*PreBalance =  m_pTradeSpi->m_Account.PreBalance;
		*CloseProfit =  m_pTradeSpi->m_Account.CloseProfit;
		*PositionProfit =  m_pTradeSpi->m_Account.PositionProfit;
		*Margin = m_pTradeSpi->m_Account.ExchangeMargin;
		*Commission =  m_pTradeSpi->m_Account.Commission;
		*Balance =  m_pTradeSpi->m_Account.Balance;
		*Available =  m_pTradeSpi->m_Account.Available;
		m_pTradeSpi->m_AcctLock.Unlock();
	}

	bool GetInstrument(FutureInstrumentField* pIns){
		return m_pTradeSpi->GetInstrument(pIns);
	}

	void GetInstrument(const char* pszProductID,FutureInstrumentField** ppIns,int* pCount){
		m_pTradeSpi->GetInstrument(pszProductID,ppIns,pCount);
	}

	void GetLongPos(const char *pszCode,int& nTotal,int& nClosable,int& nTodayClosable){
		m_pTradeSpi->GetLongPos(pszCode,nTotal,nClosable,nTodayClosable);
	}

	void GetShortPos(const char *pszCode,int& nTotal,int& nClosable,int& nTodayClosable){
		m_pTradeSpi->GetShortPos(pszCode,nTotal,nClosable,nTodayClosable);
	}

	void GetLongPos(const char *pszCode,int& nTotal,int& nClosable,int& nTodayClosable,double& fAvgOpenPrice){
		m_pTradeSpi->GetLongPos(pszCode,nTotal,nClosable,nTodayClosable,fAvgOpenPrice);
	}

	void GetShortPos(const char *pszCode,int& nTotal,int& nClosable,int& nTodayClosable,double& fAvgOpenPrice){
		m_pTradeSpi->GetShortPos(pszCode,nTotal,nClosable,nTodayClosable,fAvgOpenPrice);
	}


	void SendInstrumentStatus(CThostFtdcInstrumentStatusField* pStatus){
		if(m_pPolicy){
			CThostFtdcInstrumentStatusField* pNew = new CThostFtdcInstrumentStatusField();
			memcpy(pNew,pStatus,sizeof(CThostFtdcInstrumentStatusField));

			m_pPolicy->AddMsg(MSG_FINSTRUMENT_SATATUS2,(void*)pNew);
		}
	}

	virtual const char * GetTradingDay(){
		return m_pTradeSpi->GetTradingDay();
	}

	void RegisterTimer(int nMilliSecondsAfter,int nAction,OrderField* pOrder);

	void Release();

	void LogError(const char* pszError);

	void LogInfo(const char *pszError);

	void LogDebug(const char *pszError);

	int GetCancelOrdersCount(const char *pszIns){
		return m_pTradeSpi->GetCancelOrdersCount(pszIns);
	}

	virtual void SetSHFE( bool bSHFE );

};

class CCtpFutureAccount:public IFutureAccount{
private:
	char m_cUser[64];
	char m_cPassword[64];

public:

	CCtpFutureAccount(){
		m_cUser[0]='\0';
		m_cPassword[0]='\0';
	}


	CTradeSpi* m_pTradeSpi;

	virtual const char* GetUser();

	const char* GetBrokerID(){
		return m_pTradeSpi->m_Config.GetBroker();
	}

	virtual const char* GetPassword();

	/**
	 *login to trade;
	 */
	virtual int Login(const char* UserID,const char* Password,char* ErrorMsg,int* ErrorID);

	virtual int Logout();

	virtual IFutureTrader* CreateTrader(IPolicy* pPolicy,char cHedgeFlag='1',bool IsSHFE=false);

	virtual void Stop();

	virtual void Release();
};


extern "C"{
	IFutureAccount* CreateAccount(const char *pszConfigFile);
}


#endif // !defined(AFX_TRADESPI_H__57541734_3615_435A_B3CB_519DAF3B527B__INCLUDED_)
